#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
QNX系统文件拉取工具

该脚本用于自动将QNX系统的文件或目录拉取到本地电脑的指定目录。
工作流程：
1. 通过ADB shell进入Android环境
2. 使用busybox telnet连接至QNX系统
3. 从QNX系统再telnet至Android系统
4. 将文件从QNX系统复制到Android的/data/local/tmp目录
5. 使用ADB将文件从Android设备拉取到本地电脑
"""

import subprocess
import time
import os
import sys
import platform
import glob
import math
import shutil

# 根据操作系统选择适当的库
if platform.system() == 'Windows':
    try:
        import wexpect

        # 创建一个别名，使代码兼容
        pexpect = wexpect
        IS_WINDOWS = True
        # Windows系统下，增加调试输出
        DEBUG_MODE = True


        # wexpect库没有exceptions模块，需要自定义异常处理
        class WexpectExceptionWrapper:
            class ExceptionPexpect(Exception):
                pass


        pexpect.exceptions = WexpectExceptionWrapper
    except ImportError:
        print("错误: 在Windows系统上需要安装wexpect库。")
        print("请运行: pip install wexpect")
        exit(1)
else:
    import pexpect

    IS_WINDOWS = False
    DEBUG_MODE = True

#################################################
################ 配置参数区域 ####################
#################################################

# QNX系统上的源文件或目录路径
QNX_SOURCE_PATH = "/mnt/vendor/nfs/fota/ddd"

# 是否是文件夹
IS_DIRECTORY = True

# 如果拉取文件夹，可以指定要拉取的文件类型（使用glob模式），留空表示拉取所有文件
# 例如: "*.txt" 表示只拉取txt文件，["*.txt", "*.log"] 表示拉取txt和log文件
FILE_PATTERNS = []  # 例如: ["*.txt", "*.log", "*.bin"]

# 是否保留目录结构
# 如果为True，则目录结构将被保留
# 如果为False，则只拉取文件而不保留目录结构
PRESERVE_DIRECTORY_STRUCTURE = True

# 本地目标目录
LOCAL_TARGET_DIR = "/Users/only/Desktop/工作文档/DH/日志/测试"

# QNX系统IP地址
QNX_IP = "172.31.207.204"

# QNX登录用户名
QNX_USERNAME = "root"

# 二次telnet的目标Android IP地址
ANDROID_IP = "172.31.207.203"

# 安卓设备序列号（设备编号）
# 如果有多个安卓设备连接，则需要指定设备序列号
# 留空则使用默认连接的设备（适用于只连接了一个设备的情况）
ANDROID_DEVICE_SERIAL = ""

# ADB路径设置
# 如果为空，将自动查找adb路径；否则使用指定的路径
ADB_PATH = ""

# 大文件阈值（单位：字节）
# 超过此大小的文件将使用dd命令传输而不是cp命令
# 默认值为500MB
LARGE_FILE_THRESHOLD = 500 * 1024 * 1024

# DD命令块大小（单位：字节）
# 使用DD命令传输时的块大小
DD_BLOCK_SIZE = 8 * 1024 * 1024  # 8MB

# 是否在传输完成后清理Android上的临时文件
# 设置为True将在传输成功后删除/data/local/tmp目录中的临时文件
CLEANUP_TEMP_FILES = True


#################################################
################ 程序代码区域 ####################
#################################################

def find_adb_path():
    """
    查找adb命令的路径

    Returns:
        adb命令的完整路径，如果未找到则返回None
    """
    # 首先检查是否已经设置了ADB_PATH
    if ADB_PATH and os.path.exists(ADB_PATH) and os.access(ADB_PATH, os.X_OK):
        return ADB_PATH

    # 检查环境变量中的adb
    adb_from_path = shutil.which('adb')
    if adb_from_path:
        return adb_from_path

    # 检查常见的adb安装路径
    common_paths = [
        # macOS 常见路径
        '/usr/local/bin/adb',
        os.path.expanduser('~/Library/Android/sdk/platform-tools/adb'),
        '/Applications/Android Studio.app/Contents/sdk/platform-tools/adb',
        # Linux 常见路径
        '/usr/bin/adb',
        '/opt/android-sdk/platform-tools/adb',
        # Windows 常见路径
        'C:\\Program Files\\Android\\sdk\\platform-tools\\adb.exe',
        'C:\\Program Files (x86)\\Android\\sdk\\platform-tools\\adb.exe',
        # 用户自定义安装路径
        os.path.expanduser('~/android-sdk/platform-tools/adb'),
        os.path.expanduser('~/Android/Sdk/platform-tools/adb')
    ]

    for path in common_paths:
        if os.path.exists(path) and os.access(path, os.X_OK):
            return path

    return None


def format_size(size_bytes):
    """
    将字节大小格式化为人类可读的格式

    Args:
        size_bytes: 文件大小（字节）

    Returns:
        格式化后的字符串
    """
    if size_bytes == 0:
        return "0B"
    size_names = ("B", "KB", "MB", "GB", "TB")
    i = int(math.floor(math.log(size_bytes, 1024)))
    p = math.pow(1024, i)
    s = round(size_bytes / p, 2)
    return f"{s} {size_names[i]}"


class QNXFilePull:
    def __init__(self, adb_path, qnx_ip, qnx_username, android_ip, local_target_dir, preserve_structure=True,
                 android_device_serial=None):
        """
        初始化QNX文件拉取工具

        Args:
            adb_path: adb命令路径
            qnx_ip: QNX系统IP地址
            qnx_username: QNX系统登录用户名
            android_ip: 二次telnet的目标Android IP地址
            local_target_dir: 本地目标目录路径
            preserve_structure: 是否保留目录结构
            android_device_serial: 安卓设备序列号（设备编号）
        """
        self.adb_path = adb_path
        self.qnx_ip = qnx_ip
        self.qnx_username = qnx_username
        self.android_ip = android_ip
        self.local_target_dir = local_target_dir
        self.preserve_structure = preserve_structure
        self.android_device_serial = android_device_serial
        self.shell = None
        self.connected = False

    def connect(self):
        """
        建立与设备的连接

        Returns:
            bool: 连接是否成功
        """
        try:
            # 构建adb命令，根据是否指定了设备序列号添加-s参数
            adb_base_cmd = self.adb_path
            if self.android_device_serial:
                adb_base_cmd = f"{self.adb_path} -s {self.android_device_serial}"
                print(f"使用指定的安卓设备: {self.android_device_serial}")

            # 尝试执行adb root (可能会失败，但这是预期的)
            try:
                if DEBUG_MODE:
                    print(f"DEBUG: 尝试执行adb root")
                if self.android_device_serial:
                    subprocess.run([self.adb_path, "-s", self.android_device_serial, "root"], capture_output=True,
                                   text=True)
                else:
                    subprocess.run([self.adb_path, "root"], capture_output=True, text=True)
            except:
                if DEBUG_MODE:
                    print("DEBUG: adb root命令失败 (这是预期的)")
                pass

            # 2. 通过ADB shell进入Android环境
            print("正在连接到Android shell环境...")
            adb_shell_cmd = f'{adb_base_cmd} shell'
            if DEBUG_MODE:
                print(f"DEBUG: 启动命令: {adb_shell_cmd}")

            # 启动ADB shell
            try:
                # 对于Windows系统，确保以二进制模式打开子进程连接
                if IS_WINDOWS:
                    self.shell = pexpect.spawn(adb_shell_cmd, timeout=15)
                else:
                    self.shell = pexpect.spawn(adb_shell_cmd, timeout=15)

                if DEBUG_MODE:
                    print("DEBUG: adb shell已启动")
            except Exception as e:
                print(f"错误: 启动adb shell失败: {str(e)}")
                return False

            index = self.shell.expect(['#', '\$', '>', pexpect.TIMEOUT, pexpect.EOF], timeout=10)

            if index >= 3:
                print("错误: 无法连接到Android shell环境")
                return False

            if DEBUG_MODE:
                print(f"DEBUG: Android shell连接成功，提示符索引: {index}")

            # 3. 使用busybox telnet连接至QNX系统
            print(f"正在连接到QNX系统 {self.qnx_ip}...")
            if DEBUG_MODE:
                print(f"DEBUG: 发送命令: busybox telnet {self.qnx_ip}")
            self.shell.sendline(f'busybox telnet {self.qnx_ip}')

            # 等待登录提示
            if DEBUG_MODE:
                print("DEBUG: 等待telnet登录提示...")
            index = self.shell.expect(['login:', 'Connection refused', pexpect.TIMEOUT], timeout=15)
            if index == 1:
                print(f"错误: telnet连接被拒绝，请检查IP地址 {self.qnx_ip} 和网络连接")
                return False
            elif index == 2:
                print(f"错误: telnet连接超时，请检查设备 {self.qnx_ip} 是否可达")
                return False

            if DEBUG_MODE:
                print("DEBUG: 收到QNX登录提示")

            # 登录QNX系统
            print(f"使用用户名 {self.qnx_username} 登录QNX系统...")
            self.shell.sendline(self.qnx_username)

            # 确认登录成功
            if DEBUG_MODE:
                print("DEBUG: 等待登录确认...")
            index = self.shell.expect(['#', '>', 'Login incorrect', pexpect.TIMEOUT], timeout=10)
            if index == 2:
                print("错误: 登录失败，请检查用户名")
                return False
            elif index == 3:
                print("错误: 登录后无响应，请检查设备状态")
                return False

            if DEBUG_MODE:
                print(f"DEBUG: QNX登录成功，提示符索引: {index}")

            # 4. 从QNX系统再telnet至Android系统
            print(f"正在从QNX系统telnet到Android系统 {self.android_ip}...")
            if DEBUG_MODE:
                print(f"DEBUG: 发送命令: telnet {self.android_ip}")
            self.shell.sendline(f'telnet {self.android_ip}')

            # 等待连接成功
            if DEBUG_MODE:
                print("DEBUG: 等待telnet连接...")
            index = self.shell.expect(['#', '>', 'Connection refused', pexpect.TIMEOUT], timeout=15)
            if index >= 2:
                print(f"错误: 无法从QNX连接到Android系统 {self.android_ip}")
                return False

            if DEBUG_MODE:
                print(f"DEBUG: 成功从QNX连接到Android，提示符索引: {index}")

            # 确保Android临时目录存在
            print("确保Android临时目录存在...")
            if DEBUG_MODE:
                print(f"DEBUG: 发送命令: mkdir -p /data/local/tmp")
            self.shell.sendline('mkdir -p /data/local/tmp')
            self.shell.expect(['#', '>'])

            self.connected = True
            return True

        except Exception as e:
            print(f"连接错误: {str(e)}")
            if DEBUG_MODE:
                import traceback
                print(f"调试信息: {traceback.format_exc()}")
            return False

    def disconnect(self):
        """关闭连接"""
        if self.shell:
            try:
                self.shell.sendline('exit')  # 退出Android环境
                self.shell.expect(['#', '>'])
                self.shell.sendline('exit')  # 退出QNX环境
                self.shell.expect(['$', '#', '>'])
                self.shell.sendline('exit')  # 退出ADB shell
                self.shell.close()
            except:
                pass
            self.shell = None
            self.connected = False
            print("连接已关闭")

    def cleanup_temp_file(self, file_name):
        """
        清理Android设备上的临时文件

        Args:
            file_name: 要清理的文件名

        Returns:
            bool: 操作是否成功
        """
        if not CLEANUP_TEMP_FILES:
            if DEBUG_MODE:
                print("DEBUG: 临时文件清理已禁用，跳过")
            return True

        try:
            android_tmp_path = f"/data/local/tmp/{file_name}"
            print(f"正在清理临时文件 {android_tmp_path}...")

            # 构建ADB命令
            adb_cmd = [self.adb_path]
            if self.android_device_serial:
                adb_cmd.extend(["-s", self.android_device_serial])
            adb_cmd.extend(["shell", f"rm -f {android_tmp_path}"])

            if DEBUG_MODE:
                print(f"DEBUG: 执行命令: {' '.join(adb_cmd)}")

            # 执行删除命令
            try:
                rm_process = subprocess.run(
                    adb_cmd,
                    check=True,
                    capture_output=True,
                    text=True
                )
                print(f"临时文件清理成功")
                return True
            except subprocess.CalledProcessError as e:
                print(f"警告: 清理临时文件失败 - {e}")
                if hasattr(e, 'stderr') and e.stderr:
                    print(f"错误: {e.stderr}")
                return False
        except Exception as e:
            print(f"警告: 清理临时文件时发生错误: {str(e)}")
            return False

    def cleanup_temp_directory(self, file_list):
        """
        清理目录传输过程中产生的所有临时文件

        Args:
            file_list: 文件路径列表

        Returns:
            tuple: (成功数量, 失败数量)
        """
        if not CLEANUP_TEMP_FILES:
            if DEBUG_MODE:
                print("DEBUG: 临时文件清理已禁用，跳过")
            return 0, 0

        success_count = 0
        failure_count = 0

        print(f"正在清理临时目录中的文件...")

        for file_path in file_list:
            file_name = os.path.basename(file_path)
            if self.cleanup_temp_file(file_name):
                success_count += 1
            else:
                failure_count += 1

        print(f"临时文件清理完成: 成功 {success_count}, 失败 {failure_count}")
        return success_count, failure_count

    def ensure_local_dir_exists(self, dir_path):
        """
        确保本地目录存在

        Args:
            dir_path: 要创建的目录路径

        Returns:
            bool: 操作是否成功
        """
        try:
            if not os.path.exists(dir_path):
                print(f"创建本地目录 {dir_path}...")
                os.makedirs(dir_path)
            return True
        except Exception as e:
            print(f"创建本地目录错误: {str(e)}")
            return False

    def pull_single_file(self, qnx_file_path, target_subdir=None):
        """
        从QNX系统拉取单个文件到本地

        Args:
            qnx_file_path: QNX系统上的文件路径
            target_subdir: 目标子目录，如果指定则文件将被拉取到该子目录

        Returns:
            bool: 操作是否成功
        """
        try:
            # 获取文件名
            file_name = os.path.basename(qnx_file_path)
            android_tmp_path = f"/data/local/tmp/{file_name}"

            # 确定本地目标目录
            local_target_dir = self.local_target_dir
            if target_subdir:
                local_target_dir = os.path.join(local_target_dir, target_subdir)

            # 确保本地目标目录存在
            if not self.ensure_local_dir_exists(local_target_dir):
                print(f"错误: 无法创建本地目标目录 {local_target_dir}")
                return False

            # 本地目标文件路径
            local_file_path = os.path.join(local_target_dir, file_name)

            # 确保已连接
            if not self.connected:
                print("错误: 未建立连接")
                return False

            # 获取文件大小
            print(f"正在获取文件大小...")
            cmd = f'stat -c %s {qnx_file_path}'
            if DEBUG_MODE:
                print(f"DEBUG: 发送命令: {cmd}")
            self.shell.sendline(cmd)
            self.shell.expect(['#', '>', pexpect.TIMEOUT], timeout=10)
            output = self.shell.before
            if not isinstance(output, str):
                output = output.decode('utf-8', errors='replace')

            # 尝试解析文件大小
            try:
                # 处理输出中可能包含命令回显和提示符的情况
                lines = [line.strip() for line in output.split('\n') if line.strip()]
                # 找到不包含原始命令且是数字的行
                size_line = None
                for line in lines:
                    if cmd not in line and line.isdigit():
                        size_line = line
                        break

                if size_line:
                    file_size = int(size_line)
                    is_large_file = file_size > LARGE_FILE_THRESHOLD
                    print(
                        f"文件大小: {format_size(file_size)}" + (" (大文件，将使用dd命令传输)" if is_large_file else ""))
                else:
                    raise ValueError("无法在输出中找到文件大小")
            except (ValueError, IndexError) as e:
                if DEBUG_MODE:
                    print(f"DEBUG: 使用stat命令解析文件大小失败 - {str(e)}")
                    print(f"DEBUG: 原始输出: {output}")

                # 尝试使用wc命令作为备选方法获取文件大小
                print(f"尝试使用wc命令获取文件大小...")
                cmd = f'wc -c {qnx_file_path}'
                if DEBUG_MODE:
                    print(f"DEBUG: 发送命令: {cmd}")
                self.shell.sendline(cmd)
                self.shell.expect(['#', '>', pexpect.TIMEOUT], timeout=10)
                wc_output = self.shell.before.decode('utf-8', errors='replace')

                try:
                    # 解析wc命令输出
                    wc_lines = wc_output.split('\n')
                    for line in wc_lines:
                        if qnx_file_path in line:
                            # wc输出格式通常是: "大小 文件名"
                            parts = line.strip().split()
                            if len(parts) >= 2 and parts[0].isdigit():
                                file_size = int(parts[0])
                                is_large_file = file_size > LARGE_FILE_THRESHOLD
                                print(f"文件大小: {format_size(file_size)}" + (
                                    " (大文件，将使用dd命令传输)" if is_large_file else ""))
                                break
                    else:
                        raise ValueError("无法从wc输出中解析文件大小")
                except (ValueError, IndexError) as e2:
                    if DEBUG_MODE:
                        print(f"DEBUG: 使用wc命令解析文件大小也失败 - {str(e2)}")
                        print(f"DEBUG: 原始wc输出: {wc_output}")
                    print(f"警告: 无法获取文件大小，将使用cp命令")
                    file_size = 0
                    is_large_file = False

            # 1. 将文件从QNX系统复制到Android的临时目录
            print(f"正在将文件从QNX系统复制到Android临时目录...")

            if is_large_file:
                # 使用dd命令传输大文件
                print(f"正在使用dd命令将大文件从 {qnx_file_path} 复制到 {android_tmp_path}...")

                # 计算块数
                blocks = (file_size + DD_BLOCK_SIZE - 1) // DD_BLOCK_SIZE

                cmd = f'dd if={qnx_file_path} of={android_tmp_path} bs={DD_BLOCK_SIZE} count={blocks}'
                if DEBUG_MODE:
                    print(f"DEBUG: 发送命令: {cmd}")
                self.shell.sendline(cmd)

                # 等待命令执行完成，这可能需要更长的时间
                timeout = max(60, file_size // (5 * 1024 * 1024))  # 每5MB至少1秒
                index = self.shell.expect(['#', '>', 'No such file', 'cannot', 'error', pexpect.TIMEOUT],
                                          timeout=timeout)

                if index >= 2:
                    print(f"错误: 使用dd复制文件失败")
                    return False
            else:
                # 使用cp命令传输小文件
                cmd = f'cp {qnx_file_path} {android_tmp_path}'
                if DEBUG_MODE:
                    print(f"DEBUG: 发送命令: {cmd}")
                self.shell.sendline(cmd)

                # 等待命令执行完成
                index = self.shell.expect(['#', '>', 'No such file', 'cannot', pexpect.TIMEOUT], timeout=15)
                if index >= 2:
                    print(f"错误: 复制文件失败")
                    return False

            # 2. 修改文件权限为644
            print(f"正在修改文件权限为644...")
            cmd = f'chmod 644 {android_tmp_path}'
            if DEBUG_MODE:
                print(f"DEBUG: 发送命令: {cmd}")
            self.shell.sendline(cmd)

            # 等待命令执行完成
            index = self.shell.expect(['#', '>', 'No such file', 'cannot', pexpect.TIMEOUT], timeout=15)
            if index >= 2:
                print(f"错误: 修改文件权限失败")
                return False

            # 3. 使用ADB将文件从Android设备拉取到本地
            print(f"正在将文件从Android设备拉取到本地...")
            try:
                # 构建ADB命令
                adb_cmd = [self.adb_path]
                if self.android_device_serial:
                    adb_cmd.extend(["-s", self.android_device_serial])
                adb_cmd.extend(["pull", android_tmp_path, local_file_path])

                if DEBUG_MODE:
                    print(f"DEBUG: 执行命令: {' '.join(adb_cmd)}")

                # 执行拉取命令
                pull_process = subprocess.run(
                    adb_cmd,
                    check=True,
                    capture_output=True,
                    text=True
                )
                print(f"文件拉取成功: {pull_process.stdout.strip()}")

                # 清理临时文件
                self.cleanup_temp_file(file_name)
                return True
            except subprocess.CalledProcessError as e:
                print(f"错误: 拉取文件失败 - {e}")
                if hasattr(e, 'stdout') and e.stdout:
                    print(f"输出: {e.stdout}")
                if hasattr(e, 'stderr') and e.stderr:
                    print(f"错误: {e.stderr}")
                return False

        except Exception as e:
            print(f"错误: {str(e)}")
            if DEBUG_MODE:
                import traceback
                print(f"调试信息: {traceback.format_exc()}")
            return False

    def pull_directory(self, qnx_dir_path, patterns=None):
        """
        从QNX系统拉取目录到本地

        Args:
            qnx_dir_path: QNX系统上的目录路径
            patterns: 文件匹配模式列表，例如 ["*.txt", "*.log"]

        Returns:
            tuple: (成功数量, 失败数量, 总数量)
        """
        try:
            # 获取目录基础名
            base_dir_name = os.path.basename(qnx_dir_path)

            # 确定本地目标目录
            if self.preserve_structure:
                local_target_base_dir = os.path.join(self.local_target_dir, base_dir_name)
                print(f"将保留目录结构，根目录将是 {local_target_base_dir}")

                # 确保本地目标根目录存在
                if not self.ensure_local_dir_exists(local_target_base_dir):
                    print(f"错误: 无法创建本地目标根目录 {local_target_base_dir}")
                    return 0, 0, 0
            else:
                local_target_base_dir = self.local_target_dir
                print(f"不保留目录结构，文件将直接放在 {local_target_base_dir}")

            # 获取QNX系统上的文件列表及其大小
            print(f"正在获取QNX系统上的文件列表...")

            # 先获取文件列表
            cmd = f'find {qnx_dir_path} -type f'
            if patterns:
                # 如果提供了匹配模式，则添加find命令的-name参数
                name_patterns = ' -o '.join([f'-name "{pattern}"' for pattern in patterns])
                cmd = f'find {qnx_dir_path} -type f \\( {name_patterns} \\)'

            if DEBUG_MODE:
                print(f"DEBUG: 发送命令: {cmd}")
            self.shell.sendline(cmd)

            # 等待命令执行完成并收集输出
            self.shell.expect(['#', '>'])
            output = self.shell.before
            if not isinstance(output, str):
                output = output.decode('utf-8', errors='replace')

            # 解析文件列表
            files_to_pull = [line.strip() for line in output.split('\n') if
                             line.strip() and line.strip().startswith('/')]

            if not files_to_pull:
                print(f"警告: 在QNX目录 '{qnx_dir_path}' 中未找到要传输的文件")
                return 0, 0, 0

            # 建立连接
            if not self.connected and not self.connect():
                print("错误: 无法建立连接")
                return 0, 0, len(files_to_pull)

            # 拉取每个文件
            success_count = 0
            failure_count = 0
            total_count = len(files_to_pull)

            print(f"开始从QNX系统拉取 {total_count} 个文件...")

            for i, file_path in enumerate(files_to_pull):
                # 获取文件大小
                cmd = f'stat -c %s {file_path}'
                if DEBUG_MODE:
                    print(f"DEBUG: 发送命令: {cmd}")
                self.shell.sendline(cmd)
                self.shell.expect(['#', '>'])
                size_output = self.shell.before
                if not isinstance(size_output, str):
                    size_output = size_output.decode('utf-8', errors='replace')

                try:
                    # 处理输出中可能包含命令回显和提示符的情况
                    lines = [line.strip() for line in size_output.split('\n') if line.strip()]
                    # 找到不包含原始命令且是数字的行
                    size_line = None
                    for line in lines:
                        if cmd not in line and line.isdigit():
                            size_line = line
                            break

                    if size_line:
                        file_size = int(size_line)
                        is_large_file = file_size > LARGE_FILE_THRESHOLD
                        size_str = format_size(file_size)
                    else:
                        raise ValueError("无法在输出中找到文件大小")
                except (ValueError, IndexError) as e:
                    if DEBUG_MODE:
                        print(f"DEBUG: 使用stat命令解析文件大小失败 - {str(e)}")
                        print(f"DEBUG: 原始输出: {size_output}")

                    # 尝试使用wc命令作为备选方法获取文件大小
                    if DEBUG_MODE:
                        print(f"DEBUG: 尝试使用wc命令获取文件大小...")
                    cmd = f'wc -c {file_path}'
                    self.shell.sendline(cmd)
                    self.shell.expect(['#', '>', pexpect.TIMEOUT], timeout=10)
                    wc_output = self.shell.before
                    if not isinstance(wc_output, str):
                        wc_output = wc_output.decode('utf-8', errors='replace')

                    try:
                        # 解析wc命令输出
                        wc_lines = wc_output.split('\n')
                        for line in wc_lines:
                            if file_path in line:
                                # wc输出格式通常是: "大小 文件名"
                                parts = line.strip().split()
                                if len(parts) >= 2 and parts[0].isdigit():
                                    file_size = int(parts[0])
                                    is_large_file = file_size > LARGE_FILE_THRESHOLD
                                    size_str = format_size(file_size)
                                    break
                        else:
                            raise ValueError("无法从wc输出中解析文件大小")
                    except (ValueError, IndexError) as e2:
                        if DEBUG_MODE:
                            print(f"DEBUG: 使用wc命令解析文件大小也失败 - {str(e2)}")
                            print(f"DEBUG: 原始wc输出: {wc_output}")
                        size_str = "未知大小"
                        is_large_file = False

                relative_path = os.path.relpath(file_path, qnx_dir_path)
                print(f"[{i + 1}/{total_count}] 正在拉取 '{relative_path}' ({size_str})" +
                      (" (大文件，将使用dd命令)" if is_large_file else ""))

                # 如果保留目录结构，计算相对路径作为子目录
                if self.preserve_structure:
                    # 获取文件相对于根目录的相对路径的目录部分
                    rel_dir = os.path.dirname(relative_path)
                    if rel_dir:
                        # 子目录是相对路径的目录部分添加到目标基目录
                        target_subdir = os.path.join(base_dir_name, rel_dir)
                    else:
                        # 如果文件在根目录，则子目录就是基目录名
                        target_subdir = base_dir_name
                else:
                    target_subdir = None

                # 确保本地目标子目录存在
                if target_subdir:
                    local_target_subdir = os.path.join(self.local_target_dir, target_subdir)
                    if not self.ensure_local_dir_exists(local_target_subdir):
                        print(f"错误: 无法创建本地目标子目录 {local_target_subdir}")
                        failure_count += 1
                        continue

                if self.pull_single_file(file_path, target_subdir):
                    success_count += 1
                else:
                    failure_count += 1

            print(f"目录拉取完成: 成功 {success_count}, 失败 {failure_count}, 总计 {total_count}")

            return success_count, failure_count, total_count

        except Exception as e:
            print(f"错误: {str(e)}")
            if DEBUG_MODE:
                import traceback
                print(f"调试信息: {traceback.format_exc()}")
            return 0, 0, 0

    def pull_path(self, qnx_path, is_directory=False, patterns=None):
        """
        从QNX系统拉取文件或目录

        Args:
            qnx_path: QNX系统上的文件或目录路径
            is_directory: 是否是目录
            patterns: 如果是目录，文件匹配模式列表

        Returns:
            bool: 操作是否成功
        """
        try:
            # 连接设备
            if not self.connect():
                print("错误: 无法建立连接")
                return False

            # 根据类型进行传输
            if is_directory:
                success, failure, total = self.pull_directory(qnx_path, patterns)
                result = success > 0 and failure == 0
            else:
                result = self.pull_single_file(qnx_path)

            return result
        finally:
            # 确保断开连接
            self.disconnect()


def main():
    # 确保QNX源路径已设置
    if not QNX_SOURCE_PATH:
        print("错误: 请在脚本中设置正确的QNX源路径 (QNX_SOURCE_PATH)")
        return 1

    # 确保本地目标目录已设置
    if not LOCAL_TARGET_DIR:
        print("错误: 请在脚本中设置正确的本地目标目录 (LOCAL_TARGET_DIR)")
        return 1

    # 确保我们有adb路径
    adb_path = find_adb_path()
    if not adb_path:
        print("错误: 无法找到adb命令。")
        print("请尝试以下解决方案:")
        print("1. 确保已安装Android Debug Bridge (adb)")
        print("2. 将adb添加到系统PATH环境变量中")
        print("3. 在脚本配置区域设置ADB_PATH变量为adb的完整路径")
        return 1

    print(f"使用adb路径: {adb_path}")

    # 创建文件拉取对象并执行拉取
    puller = QNXFilePull(
        adb_path=adb_path,
        qnx_ip=QNX_IP,
        qnx_username=QNX_USERNAME,
        android_ip=ANDROID_IP,
        local_target_dir=LOCAL_TARGET_DIR,
        preserve_structure=PRESERVE_DIRECTORY_STRUCTURE,
        android_device_serial=ANDROID_DEVICE_SERIAL
    )

    # 执行拉取
    if puller.pull_path(QNX_SOURCE_PATH, IS_DIRECTORY, FILE_PATTERNS):
        print("文件拉取成功!")
        return 0
    else:
        print("文件拉取失败。")
        return 1


if __name__ == '__main__':
    sys.exit(main())